package com.nine.aspect;

import com.nine.anno.MyRedisCache;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

@Slf4j
@Aspect
@Component
public class RedisAspect {

    @Autowired
    private RedisTemplate redisTemplate;

    @Pointcut("@annotation(com.nine.anno.MyRedisCache)")
    public void cachePointCut() {
    }

    @Around("cachePointCut()")
    public Object doCache(ProceedingJoinPoint joinPoint) {
        Object result = null;
        try {
            // 获取当前方法注解内容
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取被代理的目标对象
            Object target = joinPoint.getTarget();
            // 获取注解的方法【错误方式】，这种方式获取到的方法是接口的方法而不是具体的实现类的方法，因此是错误的。
            // Method method = signature.getMethod();
            // 获取注解的方法【正确方式】
            Method method = target.getClass().getMethod(signature.getName(), signature.getMethod().getParameterTypes());

            MyRedisCache redisCache = method.getAnnotation(MyRedisCache.class);

            // 获取 key 前缀
            String keyPrefix = redisCache.key();
            String matchEL = redisCache.match();
            long expire = redisCache.expire();

            String key = "";
            // 如果没有配置 match 属性，则使用默认的 key 前缀
            if(StringUtils.hasText(matchEL)){
                // 解析器
                SpelExpressionParser parser = new SpelExpressionParser();
                Expression expression = parser.parseExpression(matchEL);
                StandardEvaluationContext context = new StandardEvaluationContext();
                // 获取方法里面形参的值
                Object[] args = joinPoint.getArgs();
                DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
                String[] parameterNames = discoverer.getParameterNames(method);
                for (int i = 0; i < parameterNames.length; i++) {
                    log.info("参数名：{}，参数值：{}", parameterNames[i], args[i]);
                    // 将参数名和参数值放入上下文中
                    context.setVariable(parameterNames[i], args[i]);
                }
                key = keyPrefix + ":" + expression.getValue(context).toString();
            }
            else {
                key = keyPrefix;
            }
            log.info("key: {}", key);

            result = redisTemplate.opsForValue().get(key);
            if (result != null) {
                log.info("从缓存中获取数据：{}", result);
                return result;
            }

            result = joinPoint.proceed();

            if (result != null) {
                log.info("将结果数据存入到redis中：{}", result);

                if(expire > 0){
                    redisTemplate.opsForValue().set(key, result, expire, TimeUnit.SECONDS);
                }
                else {
                    redisTemplate.opsForValue().set(key, result);
                }
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return result;
    }

}
